/*
    创建者：shuxiaokai
    创建时间：2021-09-26 21:41
    模块名称：将当前项目指定文档导出到其他项目
    备注：
*/
<template>
  <s-fieldset :title="$t('将当前项目指定文档导出到其他项目')" class="fork">
    <!-- 选择区域 -->
    <div class="fork-wrap">
      <div v-flex1="30" class="left">
        <span class="orange">
          <span>{{ $t("从左侧拖拽文档到右侧，右侧也可以进行简单的拖拽") }}</span>
        </span>
        <el-divider></el-divider>
        <el-tree
          ref="sourceTree"
          class="mt-2"
          :data="sourceTreeData"
          node-key="_id"
          draggable
          :allow-drop="() => false"
          :expand-on-click-node="true"
          @node-drag-start="handleSourceDragstart"
          @node-drag-over="handleSourceNodeDragOver"
          @node-drag-end="handleSourceDragend"
        >
          <template #default="scope">
            <div class="custom-tree-node" tabindex="0">
              <!-- file渲染 -->
              <template v-if="!scope.data.isFolder">
                <template v-for="(req) in projectInfo.rules.requestMethods">
                  <span v-if="scope.data && scope.data.method && scope.data.method.toLowerCase() === req.value.toLowerCase()" :key="req.name" class="file-icon" :style="{color: req.iconColor}">{{ req.name }}</span>
                </template>
                <div class="node-label-wrap">
                  <s-emphasize class="node-top" :title="scope.data.name" :value="scope.data.name"></s-emphasize>
                </div>
              </template>
              <!-- 文件夹渲染 -->
              <template v-if="scope.data.isFolder">
                <i class="iconfont folder-icon iconweibiaoti-_huabanfuben"></i>
                <div class="node-label-wrap">
                  <s-emphasize class="node-top" :title="scope.data.name" :value="scope.data.name"></s-emphasize>
                </div>
              </template>
            </div>
          </template>
        </el-tree>
      </div>
      <div ref="target" v-flex1="30" class="right">
        <div>
          <div class="orange">
            <span>{{ $t("鼠标右键可以新增文件夹或者删除文件夹") }}</span>
          </div>
          <el-radio-group v-if="projectEnum.length < 4" v-model="targetProjectId" class="mt-2" @change="handleChangeProject">
            <el-radio v-for="(item, index) in projectEnum" :key="index" :label="item._id">{{ item.projectName }}</el-radio>
          </el-radio-group>
          <el-select v-else v-model="targetProjectId" :size="config.renderConfig.layout.size" class="mt-2" filterable @change="handleChangeProject">
            <el-option v-for="(item,index) in projectEnum" :key="index" :value="item._id" :label="item.projectName"></el-option>
          </el-select>
          <el-divider></el-divider>
          <s-loading :loading="loading" class="project-nav mt-2">
            <el-tree
              ref="targetTree"
              :data="targetTreeData"
              node-key="_id"
              draggable
              :allow-drop="checkTargetCouldDrop"
              :expand-on-click-node="true"
              :empty-text="$t('暂无文档，请在项目中添加至少一个文档')"
              @node-drag-over="handleTargetNodeOver"
              @node-drag-start="handleTargetDragStart"
              @node-drop="handleTargetDrop"
              @node-expand="clearContextmenu"
              @node-collapse="clearContextmenu"
            >
              <template #default="scope">
                <div class="custom-tree-node" tabindex="0">
                  <!-- file渲染 -->
                  <template v-if="!scope.data.isFolder">
                    <template v-for="(req) in projectInfo.rules.requestMethods">
                      <span v-if="scope.data.method.toLowerCase() === req.value.toLowerCase()" :key="req.name" class="file-icon" :style="{color: req.iconColor}">{{ req.name }}</span>
                    </template>
                    <div class="node-label-wrap">
                      <s-emphasize class="node-top" :title="scope.data.name" :value="scope.data.name"></s-emphasize>
                    </div>
                  </template>
                  <!-- 文件夹渲染 -->
                  <template v-if="scope.data.isFolder">
                    <i class="iconfont folder-icon iconweibiaoti-_huabanfuben"></i>
                    <div class="node-label-wrap">
                      <s-emphasize class="node-top" :title="scope.data.name" :value="scope.data.name"></s-emphasize>
                    </div>
                  </template>
                </div>
              </template>
            </el-tree>
          </s-loading>
        </div>
      </div>
    </div>
    <!-- <s-add-folder-dialog v-if="dialogVisible" :visible.sync="dialogVisible" :project-id="projectId" :pid="targetAddFolderMountedId" @success="handleAddFileAndFolderCb"></s-add-folder-dialog> -->
  </s-fieldset>
</template>

<script lang="ts" setup>
import { ref, Ref, onMounted, computed, ComponentPublicInstance, nextTick } from 'vue'
import { ElMessage } from 'element-plus';
import type { ApidocBanner, ApidocProjectEnum, Response } from '@@/global'
import type TreeStore from 'element-plus/lib/components/tree/src/model/tree-store'
import type { DropType } from 'element-plus/lib/components/tree/src/tree.type'
import type Node from 'element-plus/lib/components/tree/src/model/node'
import { store } from '@/store/index'
import { axios } from '@/api/api'
import { router } from '@/router/index'
import { findNextSiblingById, findParentById, findPreviousSiblingById, forEachForest, uuid } from '@/helper'
import { $t } from '@/i18n/i18n'
// import type { TreeComponentProps }  from "element-plus/lib/components/tree/src/tree.type"

type DragState = {
  dragState: {
    showDropIndicator: boolean,
    allowDrop: boolean,
    draggingNode: Node | null,
    dropNode: Node | null,
    dropType: 'before' | 'after' | 'inner' | 'none' | null,
  }
}
type TreeInstance = DragState & TreeStore & ComponentPublicInstance

/*
|--------------------------------------------------------------------------
| 全局参数，生命周期
|--------------------------------------------------------------------------
*/
//项目id
const projectId = router.currentRoute.value.query.id as string;
const projectInfo = computed(() => store.state['apidoc/baseInfo'])
/*
|--------------------------------------------------------------------------
| 项目列表信息
|--------------------------------------------------------------------------
*/
//目标树数据加载
const loading = ref(false);
//目标树数据
const targetTreeData: Ref<ApidocBanner[]> = ref([]);
//目标项目 项目id
const targetProjectId = ref('');
//根据id获取目标项目详情数据
const handleChangeProject = (pid: string | number | boolean) => {
  loading.value = true;
  const params = {
    projectId: pid,
  };
  axios.get('/api/project/doc_tree_node', { params }).then((res) => {
    targetTreeData.value = res.data;
  }).catch((err) => {
    console.error(err);
  }).finally(() => {
    loading.value = false;
  });
}
//项目列表枚举
const projectEnum: Ref<ApidocProjectEnum[]> = ref([]);
const getProjectEnum = () => {
  axios.get<Response<ApidocProjectEnum[]>, Response<ApidocProjectEnum[]>>('/api/project/project_enum').then((res) => {
    res.data.forEach((val) => {
      if (val._id !== projectId) { //过滤掉当前项目
        projectEnum.value.push(val);
      }
    })
    if (projectEnum.value.length > 0) {
      targetProjectId.value = projectEnum.value[0]._id;
      handleChangeProject(projectEnum.value[0]._id);
    }
  }).catch((err) => {
    console.error(err);
  });
}
onMounted(() => {
  getProjectEnum();
})

/*
|--------------------------------------------------------------------------
| 原树和目标树 基础数据
|--------------------------------------------------------------------------
|
*/
//目标数实例
const targetTree: Ref<(TreeInstance) | null> = ref(null);
//源树组件实例
const sourceTree: Ref<(TreeInstance) | null> = ref(null);

//源树数据
const sourceTreeData = computed(() => {
  const copyData: (ApidocBanner & { _isSource?: boolean })[] = JSON.parse(JSON.stringify(store.state['apidoc/banner'].banner));
  forEachForest(copyData, (data) => {
    data._isSource = true;
  });
  return copyData;
});
/*
|--------------------------------------------------------------------------
| 拖拽相关
|--------------------------------------------------------------------------
|
*/
//是否在源树中，如果在则取消拖拽到目标树事件
const isInSource = ref(false);
//判断目标树是否允许drop
const checkTargetCouldDrop = (draggingNode: Node, dropNode: Node, type: DropType) => {
  // let realDragNode = draggingNode || targetTree.value?.dragState.draggingNode
  // if (!realDragNode.data.isFolder && dropNode.data.isFolder && type !== "inner") { //不允许文件在文件夹前面
  //     return type !== "prev";
  // }
  // if (realDragNode.data.isFolder && !dropNode.data.isFolder) {
  //     return false;
  // }
  // if (!dropNode.data.isFolder) {
  //     return type !== "inner";
  // }
  console.log(type)
  return true;
}

//目标树dragStart
const handleTargetDragStart = (event: Event, node: Node) => {
  console.log('target start', node, event)
  isInSource.value = false;
}
//在目标树上拖拽
const handleTargetNodeOver = () => {
  console.log('target over')
  isInSource.value = false;
}
//排序目标树
const sortTargetTree = (node: Node, dropNode: Node, type: DropType) => {
  const params = {
    _id: node.data._id, //当前节点id
    pid: '', //父元素
    sort: 0, //当前节点排序效果
    projectId,
    dropInfo: {
      nodeName: node.data.name,
      nodeId: node.data._id,
      dropNodeName: dropNode.data.name,
      dropNodeId: dropNode.data._id,
      dropType: type,
    },
  };
  const pData = findParentById(targetTreeData.value, node.data._id, { idKey: '_id' });
  params.pid = pData ? pData._id : '';
  if (type === 'inner') {
    params.sort = Date.now();
  } else {
    const nextSibling = findNextSiblingById(targetTreeData.value, node.data._id, { idKey: '_id' }) || { sort: 0 };
    const previousSibling = findPreviousSiblingById(targetTreeData.value, node.data._id, { idKey: '_id' }) || { sort: 0 };
    const previousSiblingSort = previousSibling.sort || 0;
    const nextSiblingSort = nextSibling.sort || Date.now();
    params.sort = (nextSiblingSort + previousSiblingSort) / 2;
    node.data.sort = (nextSiblingSort + previousSiblingSort) / 2;
  }
  axios.put('/api/project/change_doc_pos', params).catch((err) => {
    console.error(err)
  });
}
//目标树drop
const handleTargetDrop = (dragNode: Node, dropNode: Node, type: DropType) => {
  console.log('drop111')
  if (isInSource.value) { //拖拽到目标节点又拖拽回源节点代表取消
    targetTree.value?.remove(dragNode.data);
    return;
  }
  let targetNodeSort = Date.now();
  const { _isSource } = dragNode.data;
  if (_isSource) { //从源树拖拽到目标树
    let targetMountedId = null;
    const dropNodeId = dropNode.data._id2 || dropNode.data._id;
    const dragNodeId = dragNode.data._id2 || dragNode.data._id;
    if (type === 'inner') { //拖放至内部则选择dropNode
      targetMountedId = dropNodeId;
    } else {
      const dropNodeParentNode = findParentById(targetTreeData.value, dropNodeId, { idKey: '_id' });
      const nextSibling = findNextSiblingById(targetTreeData.value, dragNodeId, { idKey: '_id' }) || { sort: 0 };
      const previousSibling = findPreviousSiblingById(targetTreeData.value, dragNodeId, { idKey: '_id' }) || { sort: 0 };
      const previousSiblingSort = previousSibling.sort || 0;
      const nextSiblingSort = nextSibling.sort || Date.now();
      targetNodeSort = (nextSiblingSort + previousSiblingSort) / 2;
      dropNode.data.sort = (nextSiblingSort + previousSiblingSort) / 2;
      if (dropNodeParentNode) { //非根节点
        targetMountedId = dropNodeParentNode._id;
      }
    }
    const selectedDocIds: string[] = [];
    forEachForest([dragNode.data], (data) => {
      selectedDocIds.push(data._id2 || data._id);
    });
    const params = {
      sourceRootId: dragNode.data._id2 || dragNode.data._id, //源节点根id
      selectedDocIds, //需要挂载的节点ids
      sourceProjectId: projectId, //源项目id
      targetProjectId: targetProjectId.value, //目标项目id
      targetMountedId, //目标挂载节点id
      targetNodeSort,
    };
    axios.post('/api/project/export/fork', params).then((res) => {
      const docsIdMap = res.data;
      forEachForest(targetTreeData.value, (data) => {
        const { _id } = data;
        const newId = docsIdMap[_id]
        if (newId) {
          data._id = newId;
        }
      });
      ElMessage.success($t('导入成功'));
    }).catch((err) => {
      console.error(err);
    });
  } else { //目标树内自己拖拽，调用排序而不是新增
    // console.log("目标树内自己拖拽")
    sortTargetTree(dragNode, dropNode, type);
  }
  dragNode.data._isSource = false;
}

//拖拽开始(源)
const handleSourceDragstart = (node: Node, event: Event) => {
  console.log('drag start', targetTree.value, event, node)
  if (targetTree.value) {
    // targetTree.value.dragState.draggingNode = { node };
  }
}
//拖拽中(源)
const handleSourceNodeDragOver = () => {
  isInSource.value = true;
}
//拖拽完毕(源)
const handleSourceDragend = (draggingNode: Node, dropNode: Node, position: unknown, event: DragEvent) => {
  // 插入一个空节点用于占位
  const emptyData = {
    _id: uuid(),
  };
  sourceTree.value?.insertBefore(emptyData, draggingNode);
  targetTree.value?.$emit('node-drag-end', event);
  nextTick(() => {
    if (sourceTree.value?.getNode(draggingNode.data)) { //没有在挂载点完成拖拽
      sourceTree.value?.remove(emptyData);
    } else { //在挂载点完成拖拽
      const data = JSON.parse(JSON.stringify(draggingNode.data));
      data._id2 = data._id2 || data._id;
      data._id = uuid();
      data._isSource = true; //当前节点还原为source
      sourceTree.value?.insertAfter(data, sourceTree.value?.getNode(emptyData));
      sourceTree.value?.remove(emptyData);
    }
  })
  // console.log(event, sourceTree.value, nextTick)
}

//清除contentmenu
const clearContextmenu = () => {
  // console.log(333)
}

</script>

<style lang="scss">
.fork {
    .el-divider--horizontal {
        margin: size(10) 0;
    }
    .fork-wrap {
        display: flex;
        .left {
            flex: 0 0 50%;
            border-right: 1px solid $gray-300;
        }
        .right {
            flex: 1;
            padding: 0 size(15);
        }
        .el-tree-node__content {
            height: size(30);
            display: flex;
            align-items: center;
        }
        .custom-tree-node {
           display: flex;
            align-items: center;
            width: 100%;
            overflow: hidden;
            height: size(30);
            &:hover {
                .more {
                    display: block;
                }
            }
            .file-icon {
                font-size: fz(14);
                margin-right: size(5);
            }
            .folder-icon {
                color: $yellow;
                flex: 0 0 auto;
                width: size(16);
                height: size(16);
                margin-right: size(5);
            }
            .node-label-wrap {
                display: flex;
                flex-direction: column;
                flex: 1;
                overflow: hidden;
                .node-top {
                    width: 100%;
                    overflow: hidden;
                    text-overflow: ellipsis;
                    white-space: nowrap;
                }
                .node-bottom {
                    color: $gray-500;
                    width: 100%;
                    overflow: hidden;
                    text-overflow: ellipsis;
                    white-space: nowrap;
                }
            }
        }
    }
    //拖拽指示器样式
    .el-tree-node.is-drop-inner {
        background: mix($theme-color, $white, 70%);
        .custom-tree-node.select-node {
            background-color: mix($theme-color, $white, 70%);
        }
    }
    .el-tree__drop-indicator {
        height: size(3);
    }
}
</style>
